package com.briup.day12;

import java.lang.reflect.Field;
import java.util.Arrays;

public class StringTest {
    public static void main(String[] args){
        /*
        在String字符串不可变后，是否每一个字符串都是一个新的对象；
         不是，JDK在JVM的方法区中设置了一个String字符串的常量池，用于存储
         和管理你在程序中使用到的字符串常量对象，这里说的常量是指字符串字面量；
         即当你在使用字符串时，如果是通过字面量的方式赋值，那么它会先在方法区的
         字符串常量池中查找 你赋值的对象在常量池中是否存在，如果存在，那么直接将
         常量池中的该对象进行返回并赋值；如果不存在，那么会先在常量池中创建该对象，
         然后将该对象赋值给引用
         */
        String a = "hello";
        // 第二次使用 "hello"字面量时，在常量池中存在该字符串对象，使用直接使用该对象
        String b = "hello";
        System.out.println(a == b);
        // 通过new关键字会直接在堆区中创建对象
        String c = new String("hello");
        System.out.println(b == c);
        // intern()方法，可以将某一个字符串对象的字符内容从堆区中放入到方法区字符串常量池内
        System.out.println(b == c.intern());
    }

    private static void test2() throws Exception{
        /*
        String字符串不可变，体现在你无论对字符串进行分割、拼接还是其他操作，
         得到的都是一个新的字符串对象，而不是原本的对象；
         为什么String字符串要设计成不可变的？
            为了提升程序的安全性
         如何设计String字符串的不可变？
            1、字符串中的字符数据都是存储在String类内部的字节数组中的，
            该字节数组是String类的成员变量，并且被private修饰，同时没有对外提供
            公共的方法访问该成员变量，所以杜绝了外界通过修改该字节数组内的数据，
            从而修改字符串数据的可能性；同时该属性还是final修饰，无法执行一个新的数组对象
            2、如果设计到拼接、分割等操作，在这些方法内部实现中，对于最终的结果
            都是通过new一个新的字符串对象进行返回的，保证了不会修改原有的字符串对象
            3、在JDK9之后，对于字符串还进行了升级，限定了在类外哪怕使用反射也无法修改字符串内容
         */
        String a = "abc";
        Field value = a.getClass().getDeclaredField("value");
        value.setAccessible(true);
        byte[] o = (byte[]) value.get(a);
        o[1] = 65;
        System.out.println(a);
    }

    private static void test() {
        // 在Java中使用String来表示 字符串，字符串通常使用""进行表示
        String a = "";
        // 字符串换行拼接
        String b = "aa" +
                "aa" +
                "bbb" +
                "ccc";
        System.out.println(b);
        // 使用文本块，使用""" ...""""
        // 文本块是支持格式的，在定义时的格式能够被正常读取
        String c = "select * form t_user where id = %s;";
        System.out.println(c);

        // 对字符串内部的进行数据替换，设置字符串模板，
        // 然后通过String类内部format方法，传入实际的数据替换模板中的占位符
        // 传入参数的个数必须和模板中占位符的个数保持一致
        // 占位符：%s以字符串形式介绍你传入的参数，%d以整数数值形式接收你传入的参数
        // 在%d中可以通过指定占位的方式进行格式化，如%02d表示这里的数据占2位，
        // 如果是10以内的数会在数值前面补0；
        // 如果是%f表示接收浮点数的数据
        String s = String.format(c, 1.0);
        System.out.println(s);

        // split()方法，能够根据你传入的正则表达式作为分割条件，
        // 分割调用本方法的字符串对象，得到一个字符串数组
        String d = "a|b-c";
        // 正则表达式中，[]表示匹配[]内部中的一个字符即可
        String[] split = d.split("[|-]");
        System.out.println(Arrays.toString(split));

        // matches()方法，能够根据你传入的正则表达式作为判断条件，
        // 查看调用本方法的字符串对象，是否符合某种规则
        // 如判断字符串对象d是否为纯数字字符
        boolean matches = d.matches("^\\d+$");
        System.out.println(matches);

        // 如果要获取字符串中某一个位置的字符，传入的参数为下标，下标是从0开始的
        // String字符串本质上就是一个字符数组，存储了大量的单个字符的集合体
        // 在jdk9之后，String内部的字符数组就改为了字节数组，提升了字符串的安全性
        // String字符串是不能变的，你在拼接字符串时，得到的是一个新的字符串对象
        System.out.println(d.charAt(1));

        // substring()方法，用于分割调用该方法的字符串对象，得到一个字符串子串
        String f = "abcdef";
        // substring()方法参数有两个，
        // 第一个参数为字符串子串的起点，从源字符串的哪一个下标开始分割
        // 第二个参数为字符串子串的结束点，该结束点不包含；也可以不传入第二个参数，
        // 如果第二个参数不穿，那么表示直接分割到字符串末尾
        String f2 = f.substring(1, 5);
        System.out.println(f2);

        // 假设有一个文件的命名如下，如何获取到该文件的后缀
        String g = "a.jpg.txt.png";
        // 先找到最后一个.的下标
        int index = g.lastIndexOf(".");
        System.out.println(index);
        System.out.println(g.substring(index));
    }
}
